<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Custom widget</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="GUI, Qt, custom widget, Ruby">
<meta name="description" content="In this part of the Ruby Qt tutorial we create a custom widget.">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>Custom widget</h1>

<p>
In this part of the Ruby Qt programming tutorial, we will create a custom widget. 
</p>

<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* NewSquare */
google_ad_slot = "0364418177";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 


<p>
Toolkits usually provide only the most common widgets like buttons, text widgets, sliders etc. 
No toolkit can provide all possible widgets. Programmers must create such widgets by themselves. 
They do it by using the drawing tools provided by the toolkit. There are two possibilities. 
A programmer can modify or enhance an existing widget. Or he can create a custom widget from scratch. 
</p>


<h2>The Burning widget</h2>

<p>
In the next example, we will create a custom burning widget. 
This widget can be seen in applications like Nero or K3B. The widget will be created from scratch. 
</p>

<pre class="code">
#!/usr/bin/ruby


# ZetCode Ruby Qt tutorial

# In this program, we create
# a custom widget
#
# @author jan bodnar
# website zetcode.com
# last modified July 2009


require 'Qt'

PANEL_HEIGHT = 30
DISTANCE = 19
LINE_WIDTH = 5
DIVISIONS = 10
FULL_CAPACITY = 700
MAX_CAPACITY = 750


class Burning &lt; Qt::Widget 


    def initialize(parent) 
        super(parent)
        
        @num = [ "75", "150", "225", "300", 
            "375", "450", "525", "600", "675" ]
            
        @redColor = Qt::Color.new 255, 175, 175
        @yellowColor = Qt::Color.new 255, 255, 184

        @parent = parent
        setMinimumHeight PANEL_HEIGHT
    end

    
    def paintEvent event

        painter = Qt::Painter.new self
        
        drawWidget painter
        painter.end
    end

    def drawWidget painter

        w = width.to_f
        

        slid_width = @parent.getCurrentWidth
        
        step = (w / DIVISIONS).round.to_f

        till = ((w / MAX_CAPACITY) * slid_width).to_f
        full = ((w / MAX_CAPACITY) * FULL_CAPACITY).to_f

        if slid_width > FULL_CAPACITY
            painter.setPen @yellowColor
            painter.setBrush Qt::Brush.new @yellowColor
            painter.drawRect Qt::RectF.new 0, 0, full, PANEL_HEIGHT
            painter.setPen @redColor
            painter.setBrush Qt::Brush.new @redColor
            painter.drawRect Qt::RectF.new full+1, 0, till-full, PANEL_HEIGHT
        else 
            if slid_width > 0
               painter.setPen @yellowColor
               painter.setBrush Qt::Brush.new @yellowColor
               painter.drawRect Qt::RectF.new 0, 0, till, PANEL_HEIGHT
            end
        end

        painter.setPen Qt::Color.new 90, 90, 90
        painter.setBrush Qt::NoBrush
        painter.drawRect 0, 0, w-1, PANEL_HEIGHT-1

        newFont = font
        newFont.setPointSize 7
        painter.setFont newFont
        
        for i in (1..@num.length)
            painter.drawLine Qt::LineF.new i*step, 1, i*step, LINE_WIDTH

            metrics = Qt::FontMetrics.new newFont

            w = metrics.width @num[i-1]
            painter.drawText(Qt::PointF.new(i*step-w/2, DISTANCE), @num[i-1])
            
        end
    end
end

class QtApp &lt; Qt::Widget 

    slots 'onChanged(int)'

    def initialize
        super
        setWindowTitle "The Burning Widget"

        initUI

        resize 370, 200
        move 300, 300
        show
    end

    def initUI
    
       @cur_width = 0
       
       @slider = Qt::Slider.new Qt::Horizontal , self
       @slider.setMaximum MAX_CAPACITY
       @slider.setGeometry 50, 50, 130, 30 

       connect(@slider, SIGNAL("valueChanged(int)"), self, SLOT("onChanged(int)"))
       
       vbox = Qt::VBoxLayout.new self
       hbox = Qt::HBoxLayout.new

       vbox.addStretch 1

       @widget = Burning.new self
       hbox.addWidget @widget, 0

       vbox.addLayout hbox

       setLayout vbox
    end

    def onChanged val
        @cur_width = val
        @widget.repaint
    end

    def getCurrentWidth
      return @cur_width
    end
end


app = Qt::Application.new ARGV
QtApp.new
app.exec
</pre>

<p>
In this file, we create the Burning widget. 
</p>

<pre class="explanation">
class Burning &lt; Qt::Widget 
</pre>

<p>
The custom widget is based on the <code>Widget</code> widget. 
</p>

<pre class="explanation">
PANEL_HEIGHT = 30
DISTANCE = 19
LINE_WIDTH = 5
DIVISIONS = 10
FULL_CAPACITY = 700
MAX_CAPACITY = 750
</pre>

<p>
These are important constants. The <code>PANEL_HEIGHT</code> defines the height
for the custom widget. 
The <code>DISTANCE</code> is the distance of the numbers on the scale from the top
of their parent border. The <code>LINE_WIDTH</code> is the vertical line width.
The <code>DIVISIONS</code> is the number of parts of the
scale. The <code>FULL_CAPACITY</code> is the maximum capacity of the media. After
it is reached, overburning happens. This is visualized by a red color. 
The <code>MAX_CAPACITY</code> is the maximum capacity of a medium. 
</p>

<pre class="explanation">
@num = [ "75", "150", "225", "300", 
    "375", "450", "525", "600", "675" ]
</pre>

<p>
We use these numbers to build the scale of the Burning widget. 
</p>

<pre class="explanation">
def paintEvent event

    painter = Qt::Painter.new self

    drawWidget painter
    painter.end
end
</pre>

<p>
The drawing of the custom widget is delegated to the <code>drawWidget</code> method.
</p>


<pre class="explanation">
slid_width = @parent.getCurrentWidth
</pre>

<p>
We use it to get the currently selected slider value.
</p>

<pre class="explanation">
w = width.to_f
</pre>

<p>
We get the width of the widget. The width of the custom widget is dynamic. It can be
resized by a user. 
</p>

<pre class="explanation">
till = ((w / MAX_CAPACITY) * slid_width).to_f
full = ((w / MAX_CAPACITY) * FULL_CAPACITY).to_f
</pre>

<p>
We use the w variable to do the transformations. Between 
the values of the scale and the custom widget's measures. Note that we use floating
point values. We get greater precision in drawing. 
</p>

<pre class="explanation">
painter.setPen @redColor
painter.setBrush Qt::Brush.new @redColor
painter.drawRect Qt::RectF.new full+1, 0, till-full, PANEL_HEIGHT
</pre>

<p>
These three lines draw the red rectangle, indicating the overburning. 
</p>

<pre class="explanation">
painter.drawRect 0, 0, w-1, PANEL_HEIGHT-1
</pre>

<p>
This is the perimeter of the widget. The outside rectangle. 
</p>

<pre class="explanation">
painter.drawLine Qt::LineF.new i*step, 1, i*step, LINE_WIDTH
</pre>

<p>
Here we draw the small vertical lines. 
</p>

<pre class="explanation">
w = metrics.width @num[i-1]
painter.drawText(Qt::PointF.new(i*step-w/2, DISTANCE), @num[i-1])
</pre>

<p>
Here we draw the numbers of the scale. To precisely position the numbers,
we must get the width of the string. 
</p>

<pre class="explanation">
@widget = Burning.new self
hbox.addWidget @widget, 0
</pre>

<p>
We create the instance of the Burning widget and add
it to the horizontal box. 
</p>

<pre class="explanation">
def onChanged val
    @cur_width = val
    @widget.repaint
end
</pre>

<p>
When the value of the slider changes, we store it inside the
<code>@cur_width</code> variable and repaint the custom widget. 
</p>

<pre class="explanation">
def getCurrentWidth
return @cur_width
end
</pre>

<p>
This method is called by the custom widget to get the actual slider
value. 
</p>

<img src="/img/gui/qtruby/burning.png" alt="The Burning widget">
<div class="figure">Figure: The Burning widget</div>


<p>
In this part of the Ruby Qt tutorial, we have demonstrated how to create a custom
widget. 
</p>

<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div> 
<br>


<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡ 
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified July 8, 2009  <span class="copyright">&copy; 2007 - 2013 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>

